package com.wan.data.mysql.orm.repository;

import com.github.benmanes.caffeine.cache.*;
import com.wan.core.timer.WTimer;
import com.wan.data.mysql.orm.entity.Entity;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.io.Serializable;
import java.time.Duration;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@CacheNote
public class CacheRepository<PK extends Serializable,E extends Entity<PK>> extends DelayRepository<PK,E>{
    private final LoadingCache<PK, E> caches;

    public CacheRepository() {
        super();
        CacheNote cacheNote = this.getClass().getAnnotation(CacheNote.class);
        if(cacheNote == null){
            cacheNote = CacheRepository.class.getAnnotation(CacheNote.class);
        }
        long period = cacheNote.period();
        long timeout = Math.max(cacheNote.timeout(), TimeUnit.MINUTES.toMillis(1));
        int capacity = Math.max(cacheNote.capacity(),100);
        caches = Caffeine.newBuilder()
                //在内存不够时引用对象会被GC
                .softValues()
                //过期时间
                .expireAfterAccess(timeout,TimeUnit.MILLISECONDS)
                //容量
                .initialCapacity(capacity)
                //移除监听
                .removalListener((RemovalListener<PK, E>) (key, value, cause) -> {
                    /*
                     * EXPLICIT 手动调用invalidate或remove等方法
                     * REPLACED 调用put等方法进行修改
                     */
                    if (value != null && cause != RemovalCause.EXPLICIT && cause != RemovalCause.REPLACED) {
                        CacheRepository.super.save(value);
                    }
                })
                .build(new CacheLoader<PK, E>() {
                    @Nullable
                    @Override
                    public E load(@NonNull PK k1){
                        return CacheRepository.super.get(k1);
                    }

                    @Override
                    public @NonNull Map<@NonNull PK, @NonNull E> loadAll(@NonNull Iterable<? extends @NonNull PK> keys) throws Exception {
                        return CacheRepository.super.get((Collection<PK>) keys).stream()
                                .collect(Collectors.toMap(Entity::getPk, e->e));
                    }
                });
        //定时清理
        WTimer.get().repeatLimit(node->caches.cleanUp(), Duration.ofMillis(period),-1,false);
    }

    @Override
    public E get(PK pk) {
        return caches.get(pk);
    }

    @Override
    public Collection<E> get(Collection<PK> pks) {
        return caches.getAll(pks).values();
    }

    public E getOnlyFromCache(PK pk){
        return caches.getIfPresent(pk);
    }

    /**
     * 删除是同步删除，没有做延迟处理
     */
    @Override
    public void delete(Collection<PK> list) {
        caches.invalidateAll(list);
        super.delete(list);
    }

    @Override
    public void save(Collection<E> list, boolean delay) {
        super.save(list, delay);
        list.forEach(e -> caches.put(e.getPk(),e));
    }

    @Override
    public void save(E e, boolean delay) {
        super.save(e, delay);
        caches.put(e.getPk(),e);
    }

    @Override
    public void destroy() throws Exception {
        /* 缓存数据可能太多，注释
        Collection<E> values = caches.values();
        log.info("stopping -> 缓存内对象丢入延迟仓库--> size[{}][{}]",values.size(),tableMeta.getEntityType().getCanonicalName());
        super.save(values);*/
        super.destroy();
    }
}
